
#include <scott/ExpressionResolutor.h>

BOOST_AUTO_TEST_SUITE( ExpressionResolutor_Test_Suite )

BOOST_AUTO_TEST_CASE( Resolve_SimpleType_Test )
{
	SValueSpMapSp vars( new SValueSpMap() );

	// Assert with simple type literal
	BOOST_CHECK( ExpressionResolutor::Resolve("20", vars, STypeByte)->ToString() == 
								 SByte::Create(20)->ToString() );

	BOOST_CHECK( ExpressionResolutor::Resolve("2000", vars, STypeNumber)->ToString() == 
							   SNumber::Create(2000)->ToString() );

	BOOST_CHECK( ExpressionResolutor::Resolve("'P'", vars, STypeChar)->ToString() == 
								 SChar::Create('P')->ToString() );

	BOOST_CHECK( ExpressionResolutor::Resolve("\"Hi\"", vars, STypeString)->ToString() == 
							  SString::Create("Hi")->ToString() );
}

BOOST_AUTO_TEST_CASE( Resolve_ComplexType_Test )
{
	SValueSpMapSp vars( new SValueSpMap() );

	SEnumTypeDescriptorSp colorsTd = SEnumTypeDescriptor::Create("plugin", "colors", "");
	colorsTd->AddEntry("red", 0, "red color");			
	colorsTd->AddEntry("yellow", 1, "yellow color");
	colorsTd->AddEntry("green", 2, "green color");

	SVectorTypeDescriptorSp vstringTd = SVectorTypeDescriptor::Create("plugin", "vstring", "", STypeString);

	SStructTypeDescriptorSp sstructTd = SStructTypeDescriptor::Create("plugin", "sstruct", "");
	sstructTd->AddMember("Str", "", STypeString);
	sstructTd->AddMember("Num", "", STypeNumber);
	sstructTd->AddMember("C", "", STypeChar);
	sstructTd->AddMember("B", "", STypeByte);

	// Assert with complex type literal
	BOOST_CHECK( ExpressionResolutor::Resolve("red", vars, colorsTd)->ToString() == 
			  	      SEnum::Create(colorsTd, "red")->ToString() );

	BOOST_CHECK( ExpressionResolutor::Resolve("{\"One\", \"Two\", \"Three\"}", vars, vstringTd)->ToString() == 
					SVector::Parse(vstringTd, "{\"One\", \"Two\", \"Three\"}")->ToString() );

	BOOST_CHECK( ExpressionResolutor::Resolve("{ \"Hello\", 10, 'A', 0xFD }", vars, sstructTd)->ToString() == 
		SStruct::Parse(sstructTd, "{ \"Hello\", 10, 'A', 0xFD }")->ToString() );
}

BOOST_AUTO_TEST_CASE( Resolve_Variable_Test )
{	
	// Types
	SEnumTypeDescriptorSp colorsTd = SEnumTypeDescriptor::Create("plugin", "colors", "");
	colorsTd->AddEntry("red", 0, "red color");			
	colorsTd->AddEntry("yellow", 1, "yellow color");
	colorsTd->AddEntry("green", 2, "green color");

	SVectorTypeDescriptorSp vstringTd = SVectorTypeDescriptor::Create("plugin", "vstring", "", STypeString);
	SVectorTypeDescriptorSp vnumberTd = SVectorTypeDescriptor::Create("plugin", "vnumber", "", STypeNumber);
	SVectorTypeDescriptorSp bufferTd = SVectorTypeDescriptor::Create("plugin", "buffer", "", STypeByte);
	SVectorTypeDescriptorSp vcolorsTd = SVectorTypeDescriptor::Create("plugin", "vcolors", "", colorsTd);

	SVectorTypeDescriptorSp matrixTd = SVectorTypeDescriptor::Create("plugin", "matrix", "", vnumberTd);

	SStructTypeDescriptorSp sstruct1Td = SStructTypeDescriptor::Create("plugin", "sstruct", "");
	sstruct1Td->AddMember("str", "", STypeString);
	sstruct1Td->AddMember("num", "", STypeNumber);
	sstruct1Td->AddMember("c", "", STypeChar);
	sstruct1Td->AddMember("b", "", STypeByte);

	SStructTypeDescriptorSp sstruct2Td = SStructTypeDescriptor::Create("plugin", "sstruct2", "");
	sstruct2Td->AddMember("vs", "", vstringTd);
	sstruct2Td->AddMember("buf", "", bufferTd);
	sstruct2Td->AddMember("m", "", matrixTd);
	sstruct2Td->AddMember("s1", "", sstruct1Td);

	SVectorTypeDescriptorSp vsstruct2Td = SVectorTypeDescriptor::Create("plugin", "vsstruct", "", sstruct2Td);

	// Variables
	SValueSpMapSp vars( new SValueSpMap() );
	(*vars)["b"] = SByte::Create(10);
	(*vars)["n"] = SNumber::Create(1000);
	(*vars)["c"] = SChar::Create('C');
	(*vars)["s"] = SString::Create("Hello");

	BOOST_CHECK( ExpressionResolutor::Resolve("b", vars, STypeByte) == (*vars)["b"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("n", vars, STypeNumber) == (*vars)["n"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("c", vars, STypeChar) == (*vars)["c"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("s", vars, STypeString) == (*vars)["s"] );

	(*vars)["vn"] = SVector::Parse(vnumberTd, "{1,2,3}");
	(*vars)["buf"] = SVector::Parse(bufferTd, "0x001122334455");
	(*vars)["vs"] = SVector::Parse(vstringTd, "{\"One\",\"Two\",\"Three\"}");
	(*vars)["vcol"] = SVector::Parse(vcolorsTd, "{ red, yellow, green }");

	BOOST_CHECK( ExpressionResolutor::Resolve("vn", vars, vnumberTd) == (*vars)["vn"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vn", vars) == (*vars)["vn"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vn[0]", vars) == (*vars)["vn"]->As<SVector>()->Get(0) );
	BOOST_CHECK( ExpressionResolutor::Resolve("vn[1]", vars) == (*vars)["vn"]->As<SVector>()->Get(1) );
	BOOST_CHECK( ExpressionResolutor::Resolve("vn[2]", vars) == (*vars)["vn"]->As<SVector>()->Get(2) );
	BOOST_CHECK_THROW( ExpressionResolutor::Resolve("vn[3]", vars), SException );

	BOOST_CHECK( ExpressionResolutor::Resolve("buf", vars, bufferTd) == (*vars)["buf"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("buf", vars) == (*vars)["buf"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("buf[1]", vars) == (*vars)["buf"]->As<SVector>()->Get(1) );

	BOOST_CHECK( ExpressionResolutor::Resolve("vs", vars, vstringTd) == (*vars)["vs"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vs", vars) == (*vars)["vs"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vs[1]", vars) == (*vars)["vs"]->As<SVector>()->Get(1) );

	BOOST_CHECK( ExpressionResolutor::Resolve("vcol", vars, vcolorsTd) == (*vars)["vcol"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vcol", vars) == (*vars)["vcol"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vcol[1]", vars) == (*vars)["vcol"]->As<SVector>()->Get(1) );

	(*vars)["m"] = SVector::Parse(matrixTd, "{ {1,2,3}, {4,5,6}, {7,8,9} }");

	BOOST_CHECK( ExpressionResolutor::Resolve("m", vars, vcolorsTd) == (*vars)["m"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("m", vars) == (*vars)["m"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("m[1]", vars) == (*vars)["m"]->As<SVector>()->Get(1) );
	BOOST_CHECK( ExpressionResolutor::Resolve("m[1][2]", vars) == (*vars)["m"]->As<SVector>()->Get(1)->As<SVector>()->Get(2) );
	BOOST_CHECK_THROW( ExpressionResolutor::Resolve("m[1][4]", vars), SException );

	(*vars)["s1"] = SStruct::Parse(sstruct1Td, "{ \"Hello\", 10, 'C', 0xFF }");

	BOOST_CHECK( ExpressionResolutor::Resolve("s1", vars, vcolorsTd) == (*vars)["s1"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("s1", vars) == (*vars)["s1"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("s1.num", vars) == (*vars)["s1"]->As<SStruct>()->Get<SNumber>("num") );
	BOOST_CHECK( ExpressionResolutor::Resolve("s1.str", vars) == (*vars)["s1"]->As<SStruct>()->Get<SString>("str") );
	BOOST_CHECK( ExpressionResolutor::Resolve("s1.b", vars) == (*vars)["s1"]->As<SStruct>()->Get<SByte>("b") );
	BOOST_CHECK( ExpressionResolutor::Resolve("s1.c", vars) == (*vars)["s1"]->As<SStruct>()->Get<SChar>("c") );

	(*vars)["s2"] = SStruct::Parse(sstruct2Td, "{ {\"1\",\"2\"}, 0x001122, { {1,2}, {3,4} }, { \"Hello\", 10, 'C', 0xFF } }");

	BOOST_CHECK( ExpressionResolutor::Resolve("s2", vars, sstruct2Td) == (*vars)["s2"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("s2.vs", vars) == (*vars)["s2"]->As<SStruct>()->Get("vs") );
	BOOST_CHECK( ExpressionResolutor::Resolve("s2.buf", vars) == (*vars)["s2"]->As<SStruct>()->Get("buf") );
	BOOST_CHECK( ExpressionResolutor::Resolve("s2.m", vars) == (*vars)["s2"]->As<SStruct>()->Get("m") );
	BOOST_CHECK( ExpressionResolutor::Resolve("s2.s1", vars) == (*vars)["s2"]->As<SStruct>()->Get("s1") );

	BOOST_CHECK( ExpressionResolutor::Resolve("s2.vs[1]", vars) == 
				 (*vars)["s2"]->As<SStruct>()->Get<SVector>("vs")->Get(1) );

	BOOST_CHECK( ExpressionResolutor::Resolve("s2.buf[2]", vars) == 
				 (*vars)["s2"]->As<SStruct>()->Get<SVector>("buf")->Get(2) );

	BOOST_CHECK( ExpressionResolutor::Resolve("s2.m[0]", vars) == 
				 (*vars)["s2"]->As<SStruct>()->Get<SVector>("m")->Get(0) );
	BOOST_CHECK( ExpressionResolutor::Resolve("s2.m[0][0]", vars) == 
				 (*vars)["s2"]->As<SStruct>()->Get<SVector>("m")->Get<SVector>(0)->Get(0) );

	BOOST_CHECK( ExpressionResolutor::Resolve("s2.s1.str", vars) == (*vars)["s2"]->As<SStruct>()->Get<SStruct>("s1")->Get("str") );
	BOOST_CHECK_THROW( ExpressionResolutor::Resolve("s2.s1.none", vars), SException );

	(*vars)["s2_0"] = SStruct::Parse(sstruct2Td, "{ {\"1\",\"2\"}, 0x001122, { {1,2}, {3,4} }, { \"Hello\", 10, 'C', 0xFF } }");
	(*vars)["s2_1"] = SStruct::Parse(sstruct2Td, "{ {\"1\",\"2\"}, 0x001122, { {1,2}, {3,4} }, { \"Hello\", 10, 'C', 0xFF } }");
	(*vars)["s2_2"] = SStruct::Parse(sstruct2Td, "{ {\"1\",\"2\"}, 0x001122, { {1,2}, {3,4} }, { \"Hello\", 10, 'C', 0xFF } }");
	SVectorSp vs2 = SVector::Create(vsstruct2Td);
	vs2->AddItem( (*vars)["s2_0"] );
	vs2->AddItem( (*vars)["s2_1"] );
	vs2->AddItem( (*vars)["s2_2"] );
	(*vars)["vs2"] = vs2;

	BOOST_CHECK( ExpressionResolutor::Resolve("vs2", vars) == (*vars)["vs2"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vs2[0]", vars) == (*vars)["s2_0"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vs2[1]", vars) == (*vars)["s2_1"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vs2[2]", vars) == (*vars)["s2_2"] );
	BOOST_CHECK( ExpressionResolutor::Resolve("vs2[0].buf[2]", vars) == (*vars)["s2_0"]->As<SStruct>()->Get<SVector>("buf")->Get(2) );
	BOOST_CHECK( ExpressionResolutor::Resolve("vs2[0].m[0][0]", vars) == (*vars)["s2_0"]->As<SStruct>()->Get<SVector>("m")->Get<SVector>(0)->Get(0) );
}

BOOST_AUTO_TEST_SUITE_END()
